/**
 * CW Keyer
 * 
 * The CW keyer handles either a straight key or an iambic / paddle key.
 * They all use just one analog input line. This is how it works.
 * The analog line has the internal pull-up resistor enabled. 
 * When a straight key is connected, it shorts the pull-up resistor, analog input is 0 volts
 * When a paddle is connected, the dot and the dash are connected to the analog pin through
 * a 10K and a 2.2K resistors. These produce a 4v and a 2v input to the analog pins.
 * So, the readings are as follows :
 * 0v - straight key
 * 1-2.5 v - paddle dot
 * 2.5 to 4.5 v - paddle dash
 * 2.0 to 0.5 v - dot and dash pressed
 * 
 * The keyer is written to transparently handle all these cases
 * 
 * Generating CW
 * The CW is cleanly generated by unbalancing the front-end mixer
 * and putting the local oscillator directly at the CW transmit frequency.
 * The sidetone, generated by the Arduino is injected into the volume control
 */


// in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs
#define CW_TIMEOUT (600l) 
#define PADDLE_DOT 1
#define PADDLE_DASH 2
#define PADDLE_BOTH 3
#define PADDLE_STRAIGHT 4

//we store the last padde's character 
//to alternatively send dots and dashes 
//when both are simultaneously pressed
char lastPaddle = 0;


//reads the analog keyer pin and reports the paddle
byte getPaddle(){
  int paddle = analogRead(ANALOG_KEYER);

  if (paddle > 800)         // above 4v is up
    return 0;
    
  if (paddle > 600)    // 4-3v is dot
    return PADDLE_DASH;
  else if (paddle > 300)    //1-2v is dash
    return PADDLE_DOT;
  else if (paddle > 50)
    return PADDLE_BOTH;     //both are between 1 and 2v
  else
    return PADDLE_STRAIGHT; //less than 1v is the straight key
}

/**
 * Starts transmitting the carrier with the sidetone
 * It assumes that we have called cwTxStart and not called cwTxStop
 * each time it is called, the cwTimeOut is pushed further into the future
 */
void cwKeydown(){
  keyDown = 1;                  //tracks the CW_KEY
  tone(CW_TONE, (int)sideTone); 
  digitalWrite(CW_KEY, 1);     
  cwTimeout = millis() + CW_TIMEOUT;
}

/**
 * Stops the cw carrier transmission along with the sidetone
 * Pushes the cwTimeout further into the future
 */
void cwKeyUp(){
  keyDown = 0;    //tracks the CW_KEY
  noTone(CW_TONE);
  digitalWrite(CW_KEY, 0);    
  cwTimeout = millis() + CW_TIMEOUT;
}

/**
 * The keyer handles the straight key as well as the iambic key
 * This module keeps looping until the user stops sending cw
 * if the cwTimeout is set to 0, then it means, we have to exit the keyer loop
 * Each time the key is hit the cwTimeout is pushed to a time in the future by cwKeyDown()
 */
 
void cwKeyer(){
  byte paddle;
  lastPaddle = 0;

  while(1){
    paddle = getPaddle();
    
    // do nothing if the paddle has not been touched, unless
    // we are in the cw mode and we have timed out
    if (!paddle){
      if (0 < cwTimeout && cwTimeout < millis()){
        cwTimeout = 0;
        keyDown = 0;
        stopTx();
      }

      if (!cwTimeout)
        return;

      //if a paddle was used (not a straight key) we should extend the space to be a full dash 
      //by adding two more dots long space (one has already been added at the end of the dot or dash)
      if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT)
        delay(cwSpeed * 2);

      // got back to the begining of the loop, if no further activity happens on the paddle or the straight key
      // we will time out, and return out of this routine 
      delay(5);
      continue;
    }

    Serial.print("paddle:");Serial.println(paddle);
    // if we are here, it is only because the key or the paddle is pressed
    if (!inTx){
      keyDown = 0;
      cwTimeout = millis() + CW_TIMEOUT;
      startTx(TX_CW);
      updateDisplay();
    }
    
    // star the transmission)
    // we store the transmitted character in the lastPaddle
    cwKeydown();
    if (paddle == PADDLE_DOT){
      delay(cwSpeed);
      lastPaddle = PADDLE_DOT;
    }
    else if (paddle == PADDLE_DASH){
      delay(cwSpeed * 3);
      lastPaddle = PADDLE_DASH;
    }
    else if (paddle == PADDLE_BOTH){ //both paddles down
      //depending upon what was sent last, send the other 
      if (lastPaddle == PADDLE_DOT) {
        delay(cwSpeed * 3);
        lastPaddle = PADDLE_DASH;
      }else{      
        delay(cwSpeed);
        lastPaddle = PADDLE_DOT;
      }
    }
    else if (paddle == PADDLE_STRAIGHT){
      while (getPaddle() == PADDLE_STRAIGHT)
        delay(1);
      lastPaddle = PADDLE_STRAIGHT;
    }
    cwKeyUp();
    //introduce a dot long gap between characters if the keyer was used
    if (lastPaddle != PADDLE_STRAIGHT)
      delay(cwSpeed);
  }
}
